
考虑下面的例子:

\begin{lstlisting}[style=styleCXX]
template<typename T>
class Array {
	private:
	T* data;
	...
	public:
	Array(Array<T> const&);
	Array<T>& operator= (Array<T> const&);
	void exchangeWith (Array<T>* b) {
		T* tmp = data;
		data = b->data;
		b->data = tmp;
	}
	T& operator[] (std::size_t k) {
		return data[k];
	}
	...
};

template<typename T> inline
void exchange (T* a, T* b)
{
T tmp(*a);
*a = *b;
*b = tmp;
}
\end{lstlisting}

对于简单类型，exchange()的泛型实现工作得很好。但对于具有昂贵复制操作的类型，泛型实现可能要比定制实现的开销大得多。示例中，泛型实现需要调用一次Array<T>的复制构造函数，并调用两次其复制赋值操作符。对于大型数据结构，通常涉及复制较大的内存。然而，exchangeWith()可以通过交换内部数据指针来替换。

\subsubsubsection{16.1.1\hspace{0.2cm}定制化}

成员函数exchangeWith()提供了通用的exchange()函数的有效替代，但需要使用不同的函数在以下情况下不是很方便:

\begin{enumerate}
\item 
Array类的用户必须记住多个接口，并且需要小心地使用。

\item 
泛型算法通常不能区分各种可能性。例如:

\begin{lstlisting}[style=styleCXX]
template<typename T>
void genericAlgorithm(T* x, T* y)
{
	...
	exchange(x, y); // How do we select the right algorithm?
	...
}
\end{lstlisting}

\end{enumerate}

C++模板提供了自定义函数模板和类模板的方法。对于函数模板，这是通过重载机制实现的。例如，可以重载quickExchange()函数模板:

\begin{lstlisting}[style=styleCXX]
template<typename T>
void quickExchange(T* a, T* b) // #1
{
	T tmp(*a);
	*a = *b;
	*b = tmp;
}

template<typename T>
void quickExchange(Array<T>* a, Array<T>* b) // #2
{
	a->exchangeWith(b);
}

void demo(Array<int>* p1, Array<int>* p2)
{
	int x=42, y=-7;
	quickExchange(&x, &y); // uses #1
	quickExchange(p1, p2); // uses #2
}
\end{lstlisting}

对quickExchange()的第一个调用有两个int*类型的参数，因此当用int替换T时，只有在第一个模板(\#1声明)上推导成功。相比之下，第二个调用可以与任意一个模板匹配:调用quickExchange(p1, p2)的函数是在第一个模板中用Array<int>替换T时获得，第二个模板中用int替换时也可获得。这两次替换的结果是，函数参数类型与第二次调用的参数类型完全匹配。可以得出这样的结论:调用有歧义，但是C++认为第二个模板比第一个模板“更加特化”。在其他条件相同的情况下，重载解析更倾向于使用更特化的模板，因此选择了\#2处的模板。

\subsubsubsection{16.1.2\hspace{0.2cm}语义透明}

上一节中所示的重载使用，用于实现实例化过程的定制化，但这种“透明”在很大程度上取决于实现细节。考虑quickExchange()的解决方案，虽然泛型算法和为Array<T>类型定制的算法最终都会交换指向的值，但操作的副作用不同。通过考虑一些比较struct对象交换和Array<T>交换的代码，可以说明这一点:

\begin{lstlisting}[style=styleCXX]
struct S {
	int x;
} s1, s2;

void distinguish (Array<int> a1, Array<int> a2)
{
	int* p = &a1[0];
	int* q = &s1.x;
	a1[0] = s1.x = 1;
	a2[0] = s2.x = 2;
	quickExchange(&a1, &a2); // *p == 1 after this (still)
	quickExchange(&s1, &s2); // *q == 2 after this
}
\end{lstlisting}

调用quickExchange()之后，指向第一个数组的指针p变成了指向第二个数组的指针。即使在交换操作之后，指向非数组s1的指针仍然指向s1:只交换所指向的值。前缀quick\_is有助于使用快捷方式来实现所需的操作，但通用exchange()模板仍然可以对Array<T>进行优化:

\begin{lstlisting}[style=styleCXX]
template<typename T>
void exchange (Array<T>* a, Array<T>* b)
{
	T* p = &(*a)[0];
	T* q = &(*b)[0];
	for (std::size_t k = a->size(); k-- != 0; ) {
		exchange(p++, q++);
	}
}
\end{lstlisting}

该版本与泛型代码相比的优点是不需要(可能)临时Array<T>。exchange()模板可递归调用，因此对于Array<Array<char>>这样的类型，也有良好的性能。更特化的模板版本通常不声明为内联，因为其做了相当多的工作，而原始的泛型实现是内联的，因为只进行了少量的操作。









